﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using Q.Framework.Extension;

namespace Q.Framework.Utility
{
    public interface IFilterSearch
    {
        List<FilterDefinition> FiltersDefinition { get; set; }
    }

    [Serializable]
    public class FilterSearch<T> : IFilterSearch
    {
        public List<FilterDefinition> FiltersDefinition { get; set; }
        public List<KeyValuePair<String, Object>> AutoFilters { get; set; }

        public void AddProperty(Expression<Func<T, Enum>> property, Expression<Func<T, String>> dbProperty, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");

            FiltersDefinition.Add(new FilterDefinition() { NomePropriedade = (dbProperty.Body as MemberExpression).Member.Name, DataType = FilterDataType.Enumerado, FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name, AllowedTypes = new List<FilterType>() { FilterType.Igual, FilterType.Diferente }, TypeFullName = typeof(T).GetProperty((property.Body as MemberExpression).Member.Name).PropertyType.FullName });
        }

        public void AddProperty(Expression<Func<T, String>> property, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");

            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                    FilterType.Contem,
                    FilterType.IniciaCom,
                    FilterType.NaoContem,
                    FilterType.TerminaCom
                },
                TypeFullName = typeof(String).FullName,
                DataType = FilterDataType.Texto
            });
        }

        public void AddProperty(Expression<Func<T, Decimal>> property, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");


            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                    FilterType.MaiorQue,
                    FilterType.MaiorIgualQue,
                    FilterType.MenorQue,
                    FilterType.MenorIgualQue,
                    FilterType.Entre,
                },
                TypeFullName = typeof(Decimal).FullName,
                DataType = FilterDataType.Numerico
            });
        }

        public void AddProperty(Expression<Func<T, Int32>> property, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");

            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                    FilterType.MaiorQue,
                    FilterType.MaiorIgualQue,
                    FilterType.MenorQue,
                    FilterType.MenorIgualQue,
                    FilterType.Entre,
                },
                TypeFullName = typeof(Int32).FullName,
                DataType = FilterDataType.Numerico
            });
        }

        public void AddProperty(Expression<Func<T, DateTime>> property, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");

            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                    FilterType.MaiorQue,
                    FilterType.MaiorIgualQue,
                    FilterType.MenorQue,
                    FilterType.MenorIgualQue,
                    FilterType.Entre,
                },
                TypeFullName = typeof(DateTime).FullName,
                DataType = FilterDataType.DataHora
            });
        }

        public void AddProperty(Expression<Func<T, Decimal?>> property, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");


            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                    FilterType.MaiorQue,
                    FilterType.MaiorIgualQue,
                    FilterType.MenorQue,
                    FilterType.MenorIgualQue,
                    FilterType.Entre,
                },
                TypeFullName = typeof(Decimal?).FullName,
                DataType = FilterDataType.Numerico
            });
        }

        public void AddProperty(Expression<Func<T, Int32?>> property, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");

            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                    FilterType.MaiorQue,
                    FilterType.MaiorIgualQue,
                    FilterType.MenorQue,
                    FilterType.MenorIgualQue,
                    FilterType.Entre,
                },
                TypeFullName = typeof(Int32?).FullName,
                DataType = FilterDataType.Numerico
            });
        }

        public void AddProperty(Expression<Func<T, DateTime?>> property, String friendlyName = null)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");

            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                    FilterType.MaiorQue,
                    FilterType.MaiorIgualQue,
                    FilterType.MenorQue,
                    FilterType.MenorIgualQue,
                    FilterType.Entre,
                },
                TypeFullName = typeof(DateTime?).FullName,
                DataType = FilterDataType.DataHora
            });
        }

        public void AddAggregationProperty<E>(Expression<Func<T, E>> property, AggregationFilterMode mode = AggregationFilterMode.DropDownList, LookupSearch<E> template = null, String friendlyName = null)
        {
            FiltersDefinition.Add(new FilterDefinition()
            {
                NomePropriedade = (property.Body as MemberExpression).Member.Name,
                FriendlyName = friendlyName ?? (property.Body as MemberExpression).Member.Name,
                AllowedTypes = new List<FilterType>() {
                    FilterType.Igual,
                    FilterType.Diferente,
                },
                TypeFullName = typeof(E).FullName,
                DataType = FilterDataType.Agregacao,
                AggregationMode = mode,
                LookupTemplate = template
            });
        }

        public void AddAutoFilter(Expression<Func<T, Object>> property, Object value)
        {
            if (property == null || !(property.Body is MemberExpression))
                throw new ArgumentException("property parameter is invalid");

            AutoFilters.Add(new KeyValuePair<String, Object>(property.ToString(), value));
        }
    }

    [Serializable]
    public class FilterOption
    {
        public FilterOption() { }
        public FilterOption(FilterDefinition def, Object valor, Object valor2 = null)
        {
            Definicao = def;
            Valor1 = valor;
            Valor2 = valor2;
            NomePropriedade = def.NomePropriedade;
            FriendlyName = def.FriendlyName;
        }
        public String NomePropriedade { get; private set; }
        public String FriendlyName { get; private set; }
        public FilterType Tipo { get; set; }
        public String DescricaoTipo
        {
            get { return Tipo.GetDescription(); }
        }
        public FilterDefinition Definicao { get; set; }
        public Object Valor1 { get; set; }
        public Object Valor2 { get; set; }
    }

    [Serializable]
    public class FilterDefinition
    {
        public String NomePropriedade { get; set; }
        public String FriendlyName { get; set; }
        public List<FilterType> AllowedTypes { get; set; }
        public String TypeFullName { get; set; }
        public FilterDataType DataType { get; set; }
        public AggregationFilterMode AggregationMode { get; set; }
        public ILookupSearch LookupTemplate { get; set; }
    }

    public enum FilterType
    {
        [EnumeratedDetail(Description = "Igual", Value = "=")]
        Igual,
        [EnumeratedDetail(Description = "Diferente", Value = "<>")]
        Diferente,
        [EnumeratedDetail(Description = "Contém", Value = "LIKE")]
        Contem,
        [EnumeratedDetail(Description = "Não Contém", Value = "NOT LIKE")]
        NaoContem,
        [EnumeratedDetail(Description = "Inicia Com", Value = "LIKE")]
        IniciaCom,
        [EnumeratedDetail(Description = "Termina Com", Value = "LIKE")]
        TerminaCom,
        [EnumeratedDetail(Description = "Maior que", Value = ">")]
        MaiorQue,
        [EnumeratedDetail(Description = "Menor que", Value = "<")]
        MenorQue,
        [EnumeratedDetail(Description = "Maior ou Igual a", Value = ">=")]
        MaiorIgualQue,
        [EnumeratedDetail(Description = "Menor ou igual a", Value = "<=")]
        MenorIgualQue,
        [EnumeratedDetail(Description = "Entre", Value = "BETWEEN")]
        Entre,
    }

    public enum FilterDataType
    {
        Texto,
        Numerico,
        DataHora,
        Enumerado,
        Agregacao,
    }

    public enum AggregationFilterMode
    {
        DropDownList,
        Lookup
    }
}
